package com.sq.util;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public final class AsyncUtils {

    private static Logger log = LoggerFactory.getLogger(AsyncUtils.class);

    private static RejectedExecutionHandler newRejectedExecutionHandler(String name) {
        return new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                if (!e.isShutdown()) {
                    log.error(name + " task:[" + r.toString() + "] rejected from  " + e.toString());
                }
            }
        };
    }

    private static final Map<String, ThreadPoolExecutor> pool = new ConcurrentHashMap<>();

    public static synchronized ThreadPoolExecutor newExecutor(String poolName, int corePoolSize,
                                                    int maximumPoolSize, int maxQueueSize) {
        ThreadPoolExecutor executor = pool.get(poolName);
        if (executor != null) {
            log.error("线程池[{}]已经new过了", poolName);
            return executor;
        }
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(maxQueueSize), r -> {
            Thread thread = new Thread(r);
            thread.setName(poolName + "_" + StringUtil.getRandomString(5));
            return thread;
        }, newRejectedExecutionHandler(poolName));
        pool.put(poolName, threadPoolExecutor);
        return threadPoolExecutor;
    }

    public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {
        Map<String, String> contextMap = MDC.getCopyOfContextMap();
        return CompletableFuture.runAsync(() -> {
            MDC.setContextMap(contextMap != null ? contextMap : Collections.emptyMap());
            try {
                runnable.run();
            } catch (Exception e) {
                log.error("执行异步方法出错.", e);
            } finally {
                MDC.clear();
            }
        }, executor);
    }

}
